﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Windows.Forms;

namespace KATS_Client.UI
{
    /// <summary>
    /// A window
    /// </summary>
    class Window
    {
        /// <summary>
        /// The tabs open in this window
        /// </summary>
        public List<Tab> Tabs = new List<Tab>();

        /// <summary>
        /// Window data has changed
        /// </summary>
        public event EventHandler<EventArgs> WindowChanged;


        /// <summary>
        /// Make a new tab
        /// </summary>
        /// <param name="location">Where to have the tab</param>
        Tab AddTab(Client.FileSystemObject location)
        {
            Tab tab = new Tab(this, location.Clone());
            Tabs.Add(tab);

            if (Tabs_Changed != null)
                Tabs_Changed(this, new EventArgs());

            if (WindowChanged != null)
                WindowChanged(this, new EventArgs());
            return tab;
        }

        /// <summary>
        /// Remove a tab
        /// </summary>
        /// <param name="tab">The tab to remove</param>
        void RemoveTab(Tab tab)
        {
            if (Tabs.Count == 1)
            {
                // removing the final tab
                // add a new tab to replace this one, that will be closing
                Tab newTab = new Tab(this, new Client.FileSystemObject("", tab.Directory.IsRemote, Client.FileSystemObjectTypes.Directory));
                Tabs.Add(newTab);
            }

            Tabs.Remove(tab);

            if (Tabs_Changed != null)
                Tabs_Changed(this, new EventArgs());

            if (WindowChanged != null)
                WindowChanged(this, new EventArgs());
        }


        /// <summary>
        /// Duplicate the current tab
        /// </summary>
        void DuplicateCurrentTab()
        {
            TabPage tabpage = tabControl.SelectedTab;
            Tab tab = (Tab)tabpage.Tag;
            Tab newTab = AddTab(tab.Directory);
            newTab.Refresh_Threaded();
        }

        /// <summary>
        /// Remove the current tab
        /// </summary>
        void RemoveCurrentTab()
        {
            int tabIndex = tabControl.SelectedIndex;
            Tab tab = (Tab)tabControl.TabPages[tabIndex].Tag;
            RemoveTab(tab);
        }

        /// <summary>
        /// Register a tab's events with it's window
        /// </summary>
        /// <param name="tab">The tab to register the events</param>
        public void Register(Tab tab)
        {
            tab.Directory_Changed += new EventHandler<EventArgs>(tab_Directory_Changed);
        }

        /// <summary>
        /// A tab has moved
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void tab_Directory_Changed(object sender, EventArgs e)
        {
            // so the window generally has changed
            if (this.WindowChanged != null)
                this.WindowChanged(this, new EventArgs());
        }

        /// <summary>
        /// To XML
        /// </summary>
        /// <returns>The xml</returns>
        public XElement ToXML()
        {
            XElement[] tabData = new XElement[Tabs.Count];
            for (int i = 0; i < Tabs.Count; i++)
            {
                tabData[i] = Tabs[i].Directory.ToXML();
            }
            XElement element = new XElement("Window",
                tabData,
                new XElement("ActiveTab", this.SelectedTabIndex)
                );

            return element;
        }

        /// <summary>
        /// The current tab index
        /// </summary>
        int SelectedTabIndex = -1;

        /// <summary>
        /// Is the object starting
        /// </summary>
        bool starting = true;

        /// <summary>
        /// Create a new window
        /// </summary>
        private Window()
        {
            tabControl = new TabControl();
            tabControl.Dock = DockStyle.Fill;
            tabControl.Tag = this;
            tabControl.Alignment = TabAlignment.Bottom;
            tabControl.HotTrack = true;
            tabControl.SelectedIndexChanged += new EventHandler(tabControl_SelectedIndexChanged);
            tabControl.KeyUp += new KeyEventHandler(tabControl_KeyUp);

            Tabs_Changed += new EventHandler<EventArgs>(Window_Tabs_Changed);



            WindowManager.Register(this);

            CreateWindow();
        }

        /// <summary>
        /// A key has been pressed when the tabcontrol had focus
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void tabControl_KeyUp(object sender, KeyEventArgs e)
        {
            bool h = false;
            switch (e.KeyCode)
            {
                case Keys.F3:
                    // search key
                    this.searchBar.Show();
                    h = true;
                    break;
                case Keys.F:
                    if (e.Control)
                    {
                        // Ctrl + F pressed
                        // shortcut for search
                        this.searchBar.Show();
                        h = true;

                    }
                    break;
            }
            e.Handled = h;

        }

        /// <summary>
        /// The rectangle of the tab control for this window
        /// </summary>
        public System.Drawing.Rectangle TabControl_Rectangle
        {
            get
            {
                return this.tabControl.RectangleToScreen(this.tabControl.DisplayRectangle);
            }
        }




        /// <summary>
        /// The panel that holds this window
        /// </summary>
        public Panel panel = null;

        /// <summary>
        /// The delete button
        /// </summary>
        ToolStripButton Delete = null;

        /// <summary>
        /// The refresh button
        /// </summary>
        ToolStripButton Refresh = null;

        /// <summary>
        /// The rename button
        /// </summary>
        ToolStripButton Rename = null;

        /// <summary>
        /// The create directory button
        /// </summary>
        ToolStripButton CreateDirectory = null;

        /// <summary>
        /// The up directory button
        /// </summary>
        ToolStripButton UpDirectory = null;

        /// <summary>
        /// The new tab button
        /// </summary>
        ToolStripButton NewTab = null;

        /// <summary>
        /// The delete tab button
        /// </summary>
        ToolStripButton DeleteTab = null;


        /// <summary>
        /// Create the panel for this window
        /// </summary>
        /// <param name="window">The window to create</param>
        void CreateWindow()
        {
            panel = new Panel();
            panel.Dock = DockStyle.Fill;

            ToolStrip topPanel = CreateTopPanel();
            ToolStrip sidePanel = CreateSidePanel();
            TabControl tabControl = this.tabControl;
            CreateSearchBar();



            TableLayoutPanel tlp = new TableLayoutPanel();
            tlp.RowCount = 3;
            tlp.RowStyles.Add(new RowStyle(SizeType.Absolute, 30f));
            tlp.RowStyles.Add(new RowStyle(SizeType.Percent, 1.0f));
            tlp.RowStyles.Add(new RowStyle(SizeType.Absolute, 0f));

            tlp.ColumnCount = 2;
            tlp.ColumnStyles.Add(new ColumnStyle());
            tlp.ColumnStyles.Add(new ColumnStyle());
            tlp.Dock = DockStyle.Fill;


            // the top panel
            tlp.Controls.Add(topPanel, 0, 0);
            tlp.SetColumnSpan(topPanel, 2);
            // the side panel
            tlp.Controls.Add(sidePanel, 0, 1);

            // add the table control to the screen
            tlp.Controls.Add(tabControl, 1, 1);

            tlp.Controls.Add(searchBar.SearchPanel, 0, 2);
            tlp.SetColumnSpan(searchBar.SearchPanel, 2);


            // add the table to the splitter panel
            panel.Controls.Add(tlp);
        }

        /// <summary>Initialise the search bar for this window</summary>
        void CreateSearchBar()
        {
            searchBar = new SearchBar(this);
        }

        /// <summary>Create the search bar</summary>
        public SearchBar searchBar = null;


        /// <summary>
        /// Construct the top panel
        /// </summary>
        /// <returns>The top panel tool strip</returns>
        ToolStrip CreateTopPanel()
        {
            ToolStrip panel = new ToolStrip();
            panel.Dock = DockStyle.Fill;
            panel.GripStyle = ToolStripGripStyle.Hidden;
            panel.LayoutStyle = ToolStripLayoutStyle.HorizontalStackWithOverflow;

            ToolStripLocationTextBox Location = new ToolStripLocationTextBox();
            Location.Width = 500;
            Location.Size = new System.Drawing.Size(250, Location.Size.Height);
            Location.EnterKeyPressed += new EventHandler<EventArgs>(Location_EnterKeyPressed);
            Location.EscKeyPressed += new EventHandler<EventArgs>(Location_EscKeyPressed);

            this.LocationBox = Location;

            ToolStripDropDownButton DropDownButton = new ToolStripDropDownButton();

            ToolStripButton UpButton = Generator.MakeButton("Up", "Up", Properties.Resources.UpDirectory);
            UpButton.Tag = this;
            UpButton.Click += new EventHandler(UpButton_Click);
            ToolStripButton CreateTab = Generator.MakeButton("CreateTab", "CreateTab", Properties.Resources.CreateTab);
            CreateTab.Tag = this;
            CreateTab.Click += new EventHandler(CreateTab_Click);
            ToolStripButton DeleteTab = Generator.MakeButton("DeleteTab", "DeleteTab", Properties.Resources.DeleteTab);
            DeleteTab.Tag = this;
            DeleteTab.Click += new EventHandler(DeleteTab_Click);

            ToolStripSeparator Separator = new ToolStripSeparator();


            this.UpDirectory = UpButton;
            this.NewTab = CreateTab;
            this.DeleteTab = DeleteTab;




            panel.Items.Add(Location);
            panel.Items.Add(DropDownButton);
            panel.Items.Add(UpButton);
            panel.Items.Add(Separator);
            panel.Items.Add(CreateTab);
            panel.Items.Add(DeleteTab);

            return panel;
        }

        /// <summary>
        /// The esc key has been press in the location bar
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Location_EscKeyPressed(object sender, EventArgs e)
        {
            // revert the location bar value to the address of the current tab
            Update_LocationBox();
        }

        /// <summary>
        /// The enter key has been pressed in the location bar
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Location_EnterKeyPressed(object sender, EventArgs e)
        {
            // attempt to go to the address specified by the value of the location box
            // if invalid revert to old value

            string locationBar = this.LocationBox.Text;

            locationBar = locationBar.Trim(new char[] { '\\', ' ', '\t' });

            if (locationBar == string.Empty)
            {
                // then this should convert to the My Computer screen
                // so do not add \ to the end of the location bar text
            }
            else
            {
                if (locationBar.IndexOf('\\') < 0)
                {
                    // no \ where found in the string
                    // therefore, it should be a root directory
                    // so add a \ to the end of the location bar text
                    locationBar += "\\";
                }
            }

            Client.FileSystemObject original = ((Tab)this.tabControl.SelectedTab.Tag).Directory;

            Client.FileSystemObject dir = new KATS_Client.Client.FileSystemObject(locationBar, original.IsRemote, Client.FileSystemObjectTypes.Directory);


            if (locationBar == string.Empty || Client.FileActions.Exists(dir))
            {
                // then this directory exists
                // so move the tab to this new location
                Tab tab = (Tab)this.tabControl.SelectedTab.Tag;
                tab.Directory = dir;
            }
            else
            {
                // then this directory does not exist
                // revert back to original value
                Update_LocationBox();
            }

        }

        /// <summary>
        /// The delete tab button has been clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void DeleteTab_Click(object sender, EventArgs e)
        {
            RemoveCurrentTab();
        }

        /// <summary>
        /// The create tab button has been clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void CreateTab_Click(object sender, EventArgs e)
        {
            DuplicateCurrentTab();
        }

        /// <summary>
        /// Up button clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void UpButton_Click(object sender, EventArgs e)
        {
            if (this.tabControl.SelectedTab != null)
            {
                Tab tab = (Tab)this.tabControl.SelectedTab.Tag;
                tab.MoveUpDirectory();
            }
        }


        /// <summary>
        /// Create the side panel
        /// </summary>
        /// <returns>The side panel</returns>
        ToolStrip CreateSidePanel()
        {
            ToolStrip panel = new ToolStrip();
            panel.Dock = DockStyle.Fill;
            panel.GripStyle = ToolStripGripStyle.Hidden;
            panel.LayoutStyle = ToolStripLayoutStyle.VerticalStackWithOverflow;

            ToolStripButton refresh = Generator.MakeButton("Refresh", "Refresh", Properties.Resources.Refresh);
            refresh.Tag = this;
            refresh.Click += new EventHandler(refresh_Click);
            ToolStripButton createDirectory = Generator.MakeButton("Create Directory", "Create Directory", Properties.Resources.CreateDirectory);
            createDirectory.Tag = this;
            createDirectory.Click += new EventHandler(createDirectory_Click);
            ToolStripButton rename = Generator.MakeButton("Rename", "Rename", Properties.Resources.Rename);
            rename.Tag = this;
            rename.Click += new EventHandler(rename_Click);
            ToolStripButton delete = Generator.MakeButton("Delete", "Delete", Properties.Resources.Delete);
            delete.Tag = this;
            delete.Click += new EventHandler(delete_Click);

            this.Refresh = refresh;
            this.CreateDirectory = createDirectory;
            this.Rename = rename;
            this.Delete = delete;



            panel.Items.Add(refresh);
            panel.Items.Add(createDirectory);
            panel.Items.Add(rename);
            panel.Items.Add(delete);

            return panel;
        }

        /// <summary>
        /// Delete button clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void delete_Click(object sender, EventArgs e)
        {
            if (this.tabControl.SelectedTab != null)
            {
                Tab tab = (Tab)this.tabControl.SelectedTab.Tag;
                tab.Delete();
            }
        }

        /// <summary>
        /// Rename button clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void rename_Click(object sender, EventArgs e)
        {
            if (this.tabControl.SelectedTab != null)
            {
                Tab tab = (Tab)this.tabControl.SelectedTab.Tag;
                tab.Rename();
            }
        }

        /// <summary>
        /// Create directory button clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void createDirectory_Click(object sender, EventArgs e)
        {
            if (this.tabControl.SelectedTab != null)
            {
                Tab tab = (Tab)this.tabControl.SelectedTab.Tag;
                tab.CreateDirectory();
            }
        }

        /// <summary>
        /// Refresh button clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void refresh_Click(object sender, EventArgs e)
        {
            if (this.tabControl.SelectedTab != null)
            {
                Tab tab = (Tab)this.tabControl.SelectedTab.Tag;
                tab.Refresh_Threaded();
            }
        }

        /// <summary>
        /// Refresh all sub tabs
        /// </summary>
        public void Refresh_AllTabs()
        {
            foreach (Tab t in Tabs)
            {
                t.Refresh();
            }
        }

        /// <summary>
        /// Refresh all tabs in window
        /// </summary>
        /// <param name="Remote">Refresh the remote or local tabs?</param>
        public void Refresh_AllTabs(bool Remote)
        {
            foreach (Tab t in Tabs)
            {
                if (t.Directory.IsRemote == Remote)
                {
                    t.Refresh();
                }
            }
        }

        /// <summary>
        /// The selected tab has been changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void tabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            this.SelectedTabIndex = tabControl.SelectedIndex;
            Update_LocationBox();
            if (!starting)
            {
                if (this.WindowChanged != null)
                    this.WindowChanged(this, new EventArgs());
            }
        }

        /// <summary>
        /// Create a window from its xml
        /// </summary>
        /// <param name="xml">The xml</param>
        public Window(XElement xml)
            : this()
        {
            XElement[] tabData = xml.Elements("FileSystemObject").ToArray();
            foreach (XElement x in tabData)
            {
                Client.FileSystemObject TabDirectory = new KATS_Client.Client.FileSystemObject(x);
                Tab tab = new Tab(this, TabDirectory);

                // save in list
                Tabs.Add(tab);
            }

            if (Tabs_Changed != null)
                Tabs_Changed(new object(), new EventArgs());

            XElement activeTab = xml.Element("ActiveTab");
            int activeTabNumber = int.Parse(activeTab.Value);
            this.SelectedTabIndex = activeTabNumber;

            if (this.SelectedTabIndex >= this.tabControl.TabCount)
                this.SelectedTabIndex = this.SelectedTabIndex - 1;

            this.tabControl.SelectedIndex = activeTabNumber;

            starting = false;
        }

        /// <summary>
        /// When the tabs have been changed. Update the tab control to display the appropriate tabs
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Window_Tabs_Changed(object sender, EventArgs e)
        {
            SyncTabPages();
        }

        /// <summary>
        /// Syncronise the TabPages to match the tabs
        /// </summary>
        void SyncTabPages()
        {
            // the current tab collection
            TabControl.TabPageCollection currentTabs = tabControl.TabPages;

            // go through all the current tabs and check that each still exists
            foreach (TabPage displayedTab in currentTabs)
            {
                bool TabFound = false;
                foreach (Tab tab in Tabs)
                {
                    if (tab.UniqueNumber == ((Tab)displayedTab.Tag).UniqueNumber)
                    {
                        TabFound = true;
                        break;
                    }
                }
                if (!TabFound)
                {
                    // tab has not been found, remove tab from display

                    // check if the tab to remove is the current tab
                    if (displayedTab == tabControl.SelectedTab && tabControl.TabCount > 1)
                    {
                        // it is the end tab
                        // so move in the item
                        tabControl.SelectedIndex = tabControl.TabCount - 2;
                    }
                    try
                    {
                        tabControl.TabPages.Remove(displayedTab);
                    }
                    catch (NullReferenceException)
                    { }
                }
            }
            // now all tabs that are displayed are in the Tabs list


            // check that all tabs are displayed in the control
            for (int i = 0; i < Tabs.Count; i++)
            {
                Tab tab = Tabs[i];

                // going through all the tabs in the Tabs list
                // check that all are displayed in the control

                bool found = false;
                if (i < currentTabs.Count)
                {
                    Tab investigate = (Tab)currentTabs[i].Tag;
                    if (investigate == tab)
                    {
                        found = true;
                    }
                }

                if (!found)
                {
                    // tab not found where it should be in the displayed list
                    // add tab at point where it should be

                    // find where to insert it
                    int TabPosition = i;
                    if (TabPosition < currentTabs.Count)
                    {
                        // if insert location is inside the current tab list, then insert
                        tabControl.TabPages.Insert(TabPosition, tab.TabPage);
                    }
                    else
                    {
                        // otherwise add into the TabControl
                        tabControl.TabPages.Add(tab.TabPage);
                        // and set to be the currently viewed tab
                        tabControl.SelectedTab = tab.TabPage;
                    }
                }
            }
            // now all tabs in the list are displayed on the screen

            if (tabControl.TabPages.Count == 0)
            {
                // no tabs currently in the tab control
                // exit function
                return;
            }

            // update the data displayed in the location bar for the respective side
            Update_LocationBox();
        }

        /// <summary>
        /// Update this windows location box
        /// </summary>
        public void Update_LocationBox()
        {
            if (LocationBox != null)
            {
                // the location box has been created, so give it the correct data
                this.SelectedTabIndex = tabControl.SelectedIndex;
                if (tabControl.SelectedTab == null)
                    return;
                Tab ActiveTab = (Tab)tabControl.SelectedTab.Tag;
                if (ActiveTab.Directory.IsMyComputer)
                {
                    LocationBox.Text = ActiveTab.Directory.Name;
                }
                else
                {
                    LocationBox.Text = ActiveTab.Directory.Location;
                }

                if (this.Tabs[0].Directory.IsRemote)
                {
                    // remote
                    LocationBox.BackColor = System.Drawing.Color.Black;
                    LocationBox.ForeColor = System.Drawing.Color.White;
                }
                else
                {
                    // local
                    LocationBox.BackColor = System.Drawing.Color.White;
                }

                if (LocationBox.Focused)
                {
                    LocationBox.SelectionLength = 0;
                    LocationBox.SelectionStart = LocationBox.Text.Length;
                }
            }

        }

        /// <summary>
        /// The set of tabs have been changed
        /// </summary>
        event EventHandler<EventArgs> Tabs_Changed;

        /// <summary>
        /// The tab control for this window
        /// </summary>
        public TabControl tabControl = null;

        /// <summary>
        /// The location box for this window
        /// </summary>
        ToolStripTextBox LocationBox = null;
    }
}
